from fsm import *
import re

class InfoExtractor(object):

    def __init__(self):
        def new_append_action(e):
            return lambda fsm: fsm.memory.append(e)
        def new_append_input_action(key):
            return lambda fsm: fsm.memory.append({key: fsm.input_symbol})

        open = new_append_action('{')
        close = new_append_action('}')
        f = FSM('INIT', [])
        f.add_transition_any('INIT', None, 'INIT')
        f.add_transition('{', 'INIT', open)
        f.add_transition('}', 'INIT', close)

        #namespace parsing:
        f.add_transition('namespace', 'INIT', next_state='namespace-key')
        f.add_transition_any('namespace-key', new_append_input_action('namespace'), 'INIT')
        f.add_transition('{', 'namespace-key', open)
        f.add_transition('}', 'namespace-key', close)

        #template class parsing:
        f.add_transition('template', 'INIT', None, 'template-key')
        f.add_transition('<', 'template-key', new_append_action('open-brace'), 'open-brace')
        f.add_transition('>', 'open-brace', new_append_action('close-brace'), 'close-brace')
        f.add_transition_any('open-brace')
        f.add_transition('class', 'close-brace', next_state='class-declaration')
        f.add_transition('struct', 'close-brace', next_state='class-declaration')
        f.add_transition_any('close-brace', next_state='INIT')
        f.add_transition_any('class-declaration', new_append_input_action('class'), 'INIT')
        self.f = f

    def process(self, code):
        memory = self._run_machine(code)
        return self._extract_info(memory)
    
    def _extract_info(self, memory):
        opens = 0
        current_namespace = []
        classes = []
        for v in memory:
            if 'namespace' in v:
                current_namespace.append(v['namespace'])
            elif v == '{':
                opens += 1
            elif v == '}':
                opens -= 1
                if opens == 0 and current_namespace:
                    current_namespace.pop()
            elif 'class' in v:
                classes.append("::".join(current_namespace + [v['class']]))
        return classes

    def _run_machine(self, code):
        splitted_code = [f for f in re.split(" |\n|(<)|(>)|;", code) if f]
        self.f.reset()
        self.f.process_list(splitted_code)
        return self.f.memory

